<?php

namespace App\Http\Middleware\Common;

use App\Enums\WorkOrder\ErrCode;
use App\Exceptions\Custom\CustomHttpResponseException;
use App\Http\Controllers\BaseController;
use App\Http\Middleware\OpenShop\MerchantAppAuth;
use Closure;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Facades\Route;

class LockBefore extends BaseController
{
    const LOCK_TYPE_WEB_USER_ID      = 1; // web用户id
    const LOCK_TYPE_MERCHANT_USER_ID = 2; // merchant用户id
    const LOCK_TYPE_TOTAL_REQUEST    = 3; // 总请求参数
    const LOCK_TYPE_RESTFUL_ID       = 4; // restful id
    const LOCK_TYPE_HEADER_SHOP_ID   = 5; // header shop id

    /**
     * 通过lockType获取redis key
     *
     * @param int $lockType
     * @return string
     */
    public static function getKeyByLockType(int $lockType): string
    {
        switch ($lockType) {
            case self::LOCK_TYPE_WEB_USER_ID:
                $param = getRmsUserId();
                break;
            case self::LOCK_TYPE_MERCHANT_USER_ID:
                $param = MerchantAppAuth::$userId;
                break;
            case self::LOCK_TYPE_TOTAL_REQUEST:
                $param = md5(json_encode(request()->all()));
                break;
            case self::LOCK_TYPE_RESTFUL_ID:
                $param = request()->route('id');
                break;
            case self::LOCK_TYPE_HEADER_SHOP_ID:
                $param = request()->header('shopId', 0);
                break;
            default:
                throw new CustomHttpResponseException(ErrCode::errCodeTrans(ErrCode::CODE_ERR_WORK_ORDER_NO));
        }

        return Route::currentRouteAction() . ':' . $param;
    }

    /**
     * Handle an incoming request.
     *
     * @param \Illuminate\Http\Request $request
     * @param \Closure $next
     * @return mixed
     */
    public function handle($request, Closure $next, string $param)
    {
        $arr           = explode(':', $param);
        $lockType      = (int)($arr[0] ?? 0);
        $expireSeconds = (int)($arr[1] ?? 5);

        // 加锁
        $redis_key = self::getKeyByLockType($lockType);

        if (!Redis::setnx($redis_key, time())) {

            // 防止死锁
            $lock_time = Redis::get($redis_key);
            if (($lock_time + $expireSeconds) < time()) {
                Redis::del($redis_key);
            } else {
                Log::warning("LockBefore jam key: {$redis_key}");
                return $this->fail(1, '您的请求太频繁啦，请稍后再试~');
            }
        }

        Redis::expire($redis_key, $expireSeconds);

        return $next($request);
    }
}
